package com.pf.utils;

import com.google.common.collect.Lists;
import com.pf.common.annotation.ExcelAccountMark;
import com.pf.common.annotation.ExcelDropDownOptionMark;
import com.pf.common.annotation.ExcelIdMark;
import com.pf.common.exception.BusinessException;
import com.pf.common.processor.ValidatorProcessor;
import com.pf.dto.ErrorMsgInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author PanFei
 * @version 1.0.0
 * @createTime 2022/11/27 15:58
 */
@Slf4j
public class ExcelValidatorUtils<T> {

    private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

    Map<Class<? extends Annotation>, List<Field>> fieldMap;

    public ExcelValidatorUtils(Class<T> clazz) {
        fieldMap = getFieldMap(clazz, Lists.newArrayList(ExcelIdMark.class, ExcelAccountMark.class, ExcelDropDownOptionMark.class));
    }

    /**
     * 校验excel数据
     *
     * @param rowNum
     * @param entity
     * @param errorInfoList
     * @param <T>
     */
    public <T> void validateExcel(int rowNum, T entity, List<ErrorMsgInfo> errorInfoList, ValidatorProcessor<T> validatorProcessor) {
        StringBuilder errorMsg = new StringBuilder();
        // 获取指定注解信息
        System.out.println("fieldMap=" + fieldMap);
        // 获取ID列
        validatorProcessor.validateIdColumnProcessor(errorMsg, entity);
        // 其他通用字段校验
        Set<ConstraintViolation<T>> validateSet = validator.validate(entity);
        for (ConstraintViolation violation : validateSet) {
            errorMsg.append(violation.getMessage());
        }
        // 调用后置字段（如下拉列表，账号等字段）
        validatorProcessor.afterPostProcessor(errorMsg, entity);
        if (errorMsg.length() > 0) {
            ErrorMsgInfo errorMsgInfo = ErrorMsgInfo.builder()
                    .rowNum(rowNum).errorMsg(errorMsg.toString())
                    .build();
            errorInfoList.add(errorMsgInfo);
        }
    }


    /**
     * 校验excel的ID列
     *
     * @param errorMsg         每行异常信息
     * @param entity           待校验实体类
     * @param validIdValueList 有效的ID列值
     */
    public void validateIdColumn(StringBuilder errorMsg, T entity, List<String> validIdValueList) {
        if (validIdValueList == null) {
            validIdValueList = Lists.newArrayList();
        }
        List<Field> idFieldList = fieldMap.get(ExcelIdMark.class);
        try {
            for (Field field : idFieldList) {
                field.setAccessible(true);
                String idValue = object2Str(field.get(entity));
                if (StringUtils.isNotBlank(idValue) && !validIdValueList.contains(idValue)) {
                    ExcelIdMark excelIdMark = field.getAnnotation(ExcelIdMark.class);
                    String message = excelIdMark.message();
                    message = MessageFormat.format(message, idValue);
                    errorMsg.append(message);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 校验下拉选项值
     *
     * @param errorMsg  每行异常信息
     * @param entity    待校验实体类
     * @param optionMap 下拉选项值 key为字段名，value为下拉选项值
     */
    public void validateDropDownOptionColumn(StringBuilder errorMsg, T entity, Map<String, List<String>> optionMap) {
        if (optionMap == null) {
            return;
        }
        List<Field> dropDownOptionFieldList = fieldMap.get(ExcelDropDownOptionMark.class);
        try {
            for (Field field : dropDownOptionFieldList) {
                field.setAccessible(true);
                String optionValue = object2Str(field.get(entity));
                List<String> optionList = optionMap.get(field.getName());
                if (CollectionUtils.isEmpty(optionList)) {
                    log.info(">>>>> validateDropDownOptionColumn：【{}】字段下拉选项值为空", field.getName());
                    throw new BusinessException("下拉选项值为空！");
                }
                if (StringUtils.isNotBlank(optionValue) && !optionList.contains(optionValue)) {
                    ExcelDropDownOptionMark excelDropDownOptionMark = field.getAnnotation(ExcelDropDownOptionMark.class);
                    String message = excelDropDownOptionMark.message();
                    message = MessageFormat.format(message, optionValue);
                    errorMsg.append(message);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 校验账号信息
     *
     * @param errorMsg   每行异常信息
     * @param entity     待校验实体类
     * @param accountMap 账号短-长工号集合
     */
    public void validateAccountColumn(StringBuilder errorMsg, T entity, Map<String, String> accountMap) {
        if (accountMap == null) {
            accountMap = new HashMap<>();
        }
        Collection<String> fullAccountList = accountMap.values();
        List<Field> accountFieldList = fieldMap.get(ExcelAccountMark.class);
        try {
            for (Field field : accountFieldList) {
                field.setAccessible(true);
                String accountValue = object2Str(field.get(entity));
                if (StringUtils.isBlank(accountValue)) {
                    continue;
                }
                // 长工号有空格，短工号无空格
                if (accountValue.contains(" ")) {
                    if (!fullAccountList.contains(accountValue)) {
                        ExcelAccountMark excelAccountMark = field.getAnnotation(ExcelAccountMark.class);
                        String message = excelAccountMark.message();
                        message = MessageFormat.format(message, accountValue);
                        errorMsg.append(message);
                    }
                } else {
                    if (StringUtils.isBlank(accountMap.get(accountValue))) {
                        ExcelAccountMark excelAccountMark = field.getAnnotation(ExcelAccountMark.class);
                        String message = excelAccountMark.message();
                        message = MessageFormat.format(message, accountValue);
                        errorMsg.append(message);
                    } else {
                        // 将短工号转为长工号
                        field.set(entity, accountMap.get(accountValue));
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 获取含有某些注解的字段
     *
     * @param clazz
     * @param annotationClazzList
     * @return
     */
    private static List<Field> getFieldList(Class<?> clazz, List<Class<? extends Annotation>> annotationClazzList) {
        List<Field> fieldList = Lists.newArrayList();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            for (Class annotationClazz : annotationClazzList) {
                if (field.isAnnotationPresent(annotationClazz)) {
                    fieldList.add(field);
                }
            }
        }
        return fieldList.stream().distinct().collect(Collectors.toList());
    }

    private static Map<Class<? extends Annotation>, List<Field>> getFieldMap(Class<?> clazz, List<Class<? extends Annotation>> annotationClazzList) {
        Map<Class<? extends Annotation>, List<Field>> fieldMap = new HashMap<>();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            for (Class annotationClazz : annotationClazzList) {
                if (field.isAnnotationPresent(annotationClazz)) {
                    List<Field> fieldList = fieldMap.get(annotationClazz);
                    if (CollectionUtils.isEmpty(fieldList)) {
                        fieldList = Lists.newArrayList();
                        fieldList.add(field);
                    }
                    if (!fieldList.contains(field)) {
                        fieldList.add(field);
                    }
                    fieldMap.put(annotationClazz, fieldList);
                }
            }
        }
        return fieldMap;
    }


    private static String object2Str(Object obj) {
        if (obj == null) {
            return null;
        }
        return String.valueOf(obj);
    }


}
